#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <unordered_set>
#include "../pbcxx.h"
#include "../json.hpp"
#include "../sha256.h"
#include "../zuc.h"
#include "../base16.hpp"

// auto& a = Singleton<A>::Instance();
template <typename T>
class Singleton {
  public:
    static T& Instance();
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
  private:
    Singleton() = default;
    ~Singleton() = default;
};

template <typename T>
T& Singleton<T>::Instance() {
    static T instance;
    return instance;
}

std::string H(std::string);

std::string F(std::string, std::string);

class VectorCommitment {
    pair_class pairing_;
    ele_class g1_;
    ele_class g2_;
    ele_class gt_;
    ele_class alpha_;
    std::vector<ele_class> a_;
    int r_;

public:
    VectorCommitment(int r, nlohmann::json vdsseJson);

    ele_class get_g1();

    template<class it>
    ele_class commit(it begin_it, it end_it);

    template<class container>
    ele_class commit(container& m);

    ele_class updateCommit(ele_class C, int u, ele_class m, ele_class m1);

    template<class it>
    ele_class prove(int i, it begin_it, it end_it);

    template<class container>
    ele_class prove(int i, container& m);

    template<class u_container, class m_container>
    ele_class aggregate(ele_class C, u_container& u, m_container& m);

    template<class u_container, class m_container>
    bool verify(ele_class C, u_container& u, m_container& m, ele_class pie);

};

template<class it>
ele_class VectorCommitment::commit(it begin_it, it end_it) {
    ele_class zero(pairing_, "Zr");
    zero = 0;
    ele_class C(pairing_, "G1");
    if (begin_it == end_it) return C;
    int i = 0; 
    while (begin_it != end_it && i < r_) {
        C = C * (g1_ ^ ((*begin_it) * a_[i]));
        ++begin_it;
        ++i;
    }
    return C;
}

template<class container>
ele_class VectorCommitment::commit(container& m) {
    return this->commit(m.begin(), m.end());
}

template<class it>
ele_class VectorCommitment::prove(int i, it begin_it, it end_it) {
    ele_class t(pairing_, "Zr");
    int j = 0;
    while (begin_it != end_it) {
        if (j != i) {
            t = t + ((*begin_it) * (alpha_ ^ (r_ + 1 - i + j)));
        }
        ++j;
        ++begin_it;
    }
    return (g1_ ^ t);
}

template<class container>
ele_class VectorCommitment::prove(int i, container& m) {
    return this->prove(i, m.begin(), m.end());
}

template<class u_container, class m_container>
ele_class VectorCommitment::aggregate(ele_class C, u_container& u, m_container& m) {
    ele_class res(pairing_, "G1");
    ele_class t(pairing_, "Zr");
    ele_class pie(pairing_, "G1");
    for (auto u_it = u.begin(); u_it != u.end(); ++u_it) {
        int i = *u_it;
        t.set_str(H(std::to_string(i) + C.to_string() + m[i].to_string()));
        pie = prove(i, m);
        res = res * (pie ^ t);
    }
    return res;
}

template<class u_container, class m_container>
bool VectorCommitment::verify(ele_class C, u_container& u, m_container& m, ele_class pie) {
    ele_class t(pairing_, "Zr");
    ele_class p_lhs(pairing_, "Zr");    // 左侧指数
    ele_class p_rhs(pairing_, "Zr");    // 右侧指数
    for (auto u_it = u.begin(); u_it != u.end(); ++u_it) {
        int i = *u_it;
        t.set_str(H(std::to_string(i) + C.to_string() + m[i].to_string()));
        p_lhs = p_lhs + (t * (alpha_ ^ (r_ + 1 - i)));
        p_rhs = p_rhs + (m[i] * t);
    }
    return (pairing_(C, g2_ ^ p_lhs) == (pairing_(pie, g2_) * (gt_ ^ p_rhs)));
}

class RsaAcc {
public:
    ele_class acc;
    ele_class g;
    std::unordered_set<ele_class> X;

public:
    RsaAcc(pair_class& pairing);
    // RsaAcc(const RsaAcc&) = delete;
    // RsaAcc operator=(const RsaAcc&) = delete;
    ele_class get_acc();
    ele_class insert(std::string text);
    ele_class memWitAdd(std::string text);
    bool checkWit(std::string text, ele_class wit);
};

void Setup(nlohmann::json& vdsseJson, pair_class* &pairing, vector<RsaAcc>& Acc, vector<ele_class>& V, int r, VectorCommitment* &VC);

void Update(int ind, std::string w, std::string op, nlohmann::json& vdsseJson, pair_class* &pairing, vector<RsaAcc>& Acc, vector<ele_class>& V, VectorCommitment* &VC);

void Search(std::string w, nlohmann::json& vdsseJson, vector<RsaAcc>& Acc, vector<ele_class>& V, VectorCommitment* &VC);

void Verify(std::string w, nlohmann::json& vdsseJson, vector<RsaAcc>& Acc, vector<ele_class>& V, VectorCommitment* &VC);



